#!/bin/bash

set -e

WORK_DIR="$(cd "$(dirname "$0")" && pwd)"
INSTALL_DIR="${WORK_DIR}/output"

MODULE_NAME=""
BUILD_TARGET=""
TEST_TARGET=""
TEST="OFF"

function build_googletest() {
    cd ${WORK_DIR}/third_party/googletest
    rm -rf build && mkdir build && cd build
    cmake .. -DCMAKE_CXX_FLAGS="-w" -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}
    make -j$(nproc)
    make install
}

function build_boundscheck {
    cd ${WORK_DIR}/third_party/bounds_checking_function
    make clean
    make -j$(nproc)
    cp -rfa include ${INSTALL_DIR}
    cp -rfa lib ${INSTALL_DIR}
}

function build_openssl() {
    cd ${WORK_DIR}/third_party/openssl
    grep -q "ossl_sm2_ciphertext_size" util/libcrypto.num || cat >> util/libcrypto.num << 'EOF'
ossl_sm2_ciphertext_size                5560   3_0_3   EXIST::FUNCTION:
ossl_sm2_plaintext_size                 5561   3_0_3   EXIST::FUNCTION:
ossl_sm2_encrypt                        5562   3_0_3   EXIST::FUNCTION:
ossl_sm2_decrypt                        5563   3_0_3   EXIST::FUNCTION:
EOF
    ./config linux-x86_64 -d no-shared no-module no-filenames enable-weak-ssl-ciphers --prefix=${INSTALL_DIR} --libdir=lib
    make clean
    make -j$(nproc)
    make install_dev
    cp -rfa include ${INSTALL_DIR}
    cp -rfa open_harmony_openssl_config/openssl.cnf ${INSTALL_DIR}
}

function build_crypto() {
    if [ ! -f "${INSTALL_DIR}/lib/libgtest_main.a" ]; then
        build_googletest
    fi
    if [ ! -f "${INSTALL_DIR}/lib/libboundscheck.so" ]; then
        build_boundscheck
    fi
    if [ ! -f "${INSTALL_DIR}/lib/libcrypto.a" ]; then
        build_openssl
    fi
    cd ${WORK_DIR}/security/crypto_framework
    cp -rfa ${WORK_DIR}/cmake/common.cmake .
    cp -rfa ${WORK_DIR}/cmake/crypto_framework.cmake CMakeLists.txt
    cp -rfa ${WORK_DIR}/wrapper/hks_api.h plugin/openssl_plugin/crypto_operation/rand/inc
    rm -rf build && mkdir build && cd build
    cmake .. -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}
    make -j$(nproc)
    make install
}

function build_certificate() {
    if [ ! -f "${INSTALL_DIR}/lib/libcrypto_framework.so" ]; then
        build_crypto
    fi
    cd ${WORK_DIR}/security/certificate_framework
    cp -rfa ${WORK_DIR}/wrapper/wrap_function.txt .
    cp -rfa ${WORK_DIR}/cmake/common.cmake .
    cp -rfa ${WORK_DIR}/cmake/certificate_framework.cmake CMakeLists.txt
    rm -rf build && mkdir build && cd build
    cmake .. -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}
    make -j$(nproc)
    make install
}

function build_third_party() {
    build_googletest
    build_boundscheck
    build_openssl
}

function clean_up() {
    rm -rf ${WORK_DIR}/{output,coverage}
    echo "The output directory has been cleaned."
}

function lcov_coverage() {
    rm -rf ${WORK_DIR}/coverage && mkdir -p ${WORK_DIR}/coverage && cd ${WORK_DIR}/coverage
    chmod +x ${WORK_DIR}/wrapper/llvm-gcov.sh

    lcov --gcov-tool ${WORK_DIR}/wrapper/llvm-gcov.sh --rc lcov_branch_coverage=1 --capture \
        --directory ${WORK_DIR}/security/${MODULE_NAME}/build \
        --base-directory ${WORK_DIR}/security/${MODULE_NAME} \
        --output-file coverage.info

    lcov --gcov-tool ${WORK_DIR}/wrapper/llvm-gcov.sh --rc lcov_branch_coverage=1 \
        --remove coverage.info "/usr/*" "*/include/*" "*/inc/*" \
        --output-file coverage.info

    genhtml --rc genhtml_branch_coverage=1 --function-coverage --branch-coverage \
        --output-directory report coverage.info
}

function http_server() {
    local PORT=8888
    local HOST=$(hostname -I | awk '{print $1}')
    python -m http.server ${PORT} --bind ${HOST} -d ${WORK_DIR}/coverage/report
}

function print_usage() {
    echo ""
    echo "Usage: $0 [option] [test target]"
    echo "Available options:"
    echo "  third_party                 # Build third_party module"
    echo "  crypto                      # Build crypto module (default)"
    echo "  certificate                 # Build certificate module"
    echo "  test                        # Run test and generate coverage report"
    echo "  clean                       # Cleanup output directory"
    echo "  help                        # Show help message"
    echo ""
    echo "Test programs (when using 'test' option):"
    echo "  crypto_framework_test       # Run crypto_framework_test (default)"
    echo "  certificate_framework_test  # Run certificate_framework_test"
    echo "  cf_adapter_test             # Run cf_adapter_test"
    echo "  cf_core_test                # Run cf_core_test"
    echo "  cf_sdk_test                 # Run cf_sdk_test"
    echo "  cf_version1_test            # Run cf_version1_test"
    echo ""
    echo "Examples:"
    echo "  bash $0 third_party                     # Build third_party module"
    echo "  bash $0 crypto                          # Build crypto_framework module"
    echo "  bash $0 certificate                     # Build certificate_framework module"
    echo "  bash $0 test                            # Run test with default program (crypto_framework_test)"
    echo "  bash $0 test crypto_framework_test      # Run test with crypto_framework_test"
    echo "  bash $0 test certificate_framework_test # Run test with certificate_framework_test (certificate all test)"
    echo "  bash $0 test cf_adapter_test            # Run test with cf_adapter_test"
    echo "  bash $0 test cf_core_test               # Run test with cf_core_test"
    echo "  bash $0 test cf_sdk_test                # Run test with cf_sdk_test"
    echo "  bash $0 test cf_version1_test           # Run test with cf_version1_test"
}

function parse_options() {
    for param in $@; do
        if [ "$param" == "third_party" ]; then
            BUILD_TARGET="third_party"
        elif [ "$param" == "crypto" ]; then
            BUILD_TARGET="crypto"
        elif [[ "$param" == "cert" || "$param" == "certificate" ]]; then
            BUILD_TARGET="certificate"
        elif [ "$param" == "test" ]; then
            TEST="ON"
        elif [ "$param" == "clean" ]; then
            clean_up
            exit 0
        elif [ "$param" == "help" ]; then
            print_usage
            exit 0
        elif [[ "$TEST" == "ON" && "$TEST_TARGET" == "" ]]; then
            if [[ "$param" != "crypto_framework_test" &&
                  "$param" != "certificate_framework_test" &&
                  "$param" != "cf_adapter_test" &&
                  "$param" != "cf_core_test" &&
                  "$param" != "cf_sdk_test" &&
                  "$param" != "cf_version1_test" ]]; then
                echo "Invalid test target: $param"
                print_usage
                exit 1
            fi
            TEST_TARGET="$param"
        else
            echo "Invalid option: $param"
            print_usage
            exit 1
        fi
    done
}

function build() {
    if [ "$BUILD_TARGET" == "third_party" ]; then
        build_third_party
    elif [ "$BUILD_TARGET" == "crypto" ]; then
        build_crypto
    elif [ "$BUILD_TARGET" == "certificate" ]; then
        build_certificate
    fi
}

function test() {
    export OPENSSL_CONF=${INSTALL_DIR}/openssl.cnf
    export LD_LIBRARY_PATH=${INSTALL_DIR}/lib:$LD_LIBRARY_PATH

    if [ "$TEST_TARGET" == "crypto_framework_test" ]; then
        MODULE_NAME="crypto_framework"
        if [ ! -f "${INSTALL_DIR}/bin/crypto_framework_test" ]; then
            build_crypto
        fi
    elif [[ "$TEST_TARGET" == "certificate_framework_test" ||
            "$TEST_TARGET" == "cf_adapter_test" ||
            "$TEST_TARGET" == "cf_core_test" ||
            "$TEST_TARGET" == "cf_sdk_test" ||
            "$TEST_TARGET" == "cf_version1_test" ]]; then
        MODULE_NAME="certificate_framework"
        if [[ ! -f "${INSTALL_DIR}/bin/cf_adapter_test" ||
              ! -f "${INSTALL_DIR}/bin/cf_core_test" ||
              ! -f "${INSTALL_DIR}/bin/cf_sdk_test" ||
              ! -f "${INSTALL_DIR}/bin/cf_version1_test" ]]; then
            build_certificate
        fi
    fi

    cd ${INSTALL_DIR}/bin
    if [ "$TEST_TARGET" == "certificate_framework_test" ]; then
        ./cf_adapter_test
        ./cf_core_test
        ./cf_sdk_test
        ./cf_version1_test
    elif [ "$TEST_TARGET" == "crypto_framework_test" ]; then
        ./crypto_framework_test # --gtest_filter="CryptoMdTest.CryptoFrameworkMdCreateTest001"
    else
        ./$TEST_TARGET
    fi

    lcov_coverage
    http_server
}

function main() {
    parse_options $@

    if [ "$TEST" != "ON" ]; then
        BUILD_TARGET="${BUILD_TARGET:-crypto}"
        build
    else
        TEST_TARGET="${TEST_TARGET:-crypto_framework_test}"
        test
    fi
}

main $@
